/*
 * acooly.cn Inc.
 * Copyright (c) 2016 All Rights Reserved.
 * create by zhangpu 
 * date:2016年4月4日
 *
 */
package com.acooly.module.openapi.client.provider.fuyou.security;

import com.acooly.core.utils.Encodes;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.provider.fuyou.FuyouConstants;
import com.acooly.module.safety.signature.Signer;
import com.acooly.module.safety.support.KeyPair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @author zhangpu
 */
@Service
public class FuyouRsaSigner implements Signer<String, KeyPair> {
    private static final Logger logger = LoggerFactory.getLogger(FuyouRsaSigner.class);

    private volatile PublicKey publicKey;
    private volatile PrivateKey privateKey;

    static {
        try {
//            Security.addProvider(new BouncyCastleProvider());
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("密钥初始化失败");
        }
    }

    /**** copy from fuyou sdk ********/

    @Override
    public String sign(String t, KeyPair key) {
        logger.debug("秘钥对文件:{}", key);
        logger.debug("待签字符串:{}", t);
        String result = null;
        try {
            Signature signature = Signature.getInstance("SHA1withRSA", "BC");
            signature.initSign(getPrivateKey(key.getPrivateKeySource()));
            signature.update(t.getBytes("UTF-8"));
            byte[] tByte = signature.sign();
            result = Encodes.encodeBase64(tByte);
            logger.debug("签名成功:{}", result);
        } catch (Exception e) {
            logger.error("签名失败. getWaitToSigin:{}，错误:{}", t, e.getMessage());
        }
        return result;
    }

    @Override
    public void verify(String sign, KeyPair key, String t) {
        try {
            Signature signature = Signature.getInstance("SHA1withRSA", "BC");
            signature.initVerify(getPublicKey(key.getPublicKeySource()));
            signature.update(t.getBytes("UTF-8"));
            boolean bool = signature.verify(Encodes.decodeBase64(sign));
            if (!bool) {
                throw new RuntimeException("签名未通过");
            }
            logger.debug("验签成功");
        } catch (Exception e) {
            logger.error("签名验证未通过. signType:{},requestSign:{},calcPlain:{}", getSinType(), sign, t);
            throw new ApiClientException("签名验证未通过");
        }
    }

    @Override
    public String getSinType() {
        return FuyouConstants.SIGNER_KEY;
    }

    public PublicKey getPublicKey(final String filePath) {
        if (publicKey == null) {
            synchronized (this) {
                if (publicKey == null) {
                    initPublicKey(filePath);
                }
            }
        }
        return publicKey;
    }

    public PrivateKey getPrivateKey(final String filePath) {
        if (publicKey == null) {
            synchronized (this) {
                if (publicKey == null) {
                    initPrivateKey(filePath);
                }
            }
        }
        return privateKey;
    }

    private void initPrivateKey(String filePath) {
        String base64edKey = readFile(filePath);
        try {
            KeyFactory kf = KeyFactory.getInstance("RSA", "BC");
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Encodes.decodeBase64(base64edKey));
            privateKey = kf.generatePrivate(keySpec);
        } catch (Exception e) {
            logger.error("私钥加载失败. filePath:{}, {}", filePath, e.getMessage());
        }
    }

    private void initPublicKey(String filePath) {
        String base64edKey = readFile(filePath);
        try {
            KeyFactory kf = KeyFactory.getInstance("RSA", "BC");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Encodes.decodeBase64(base64edKey));
            publicKey = kf.generatePublic(keySpec);
        } catch (Exception e) {
            logger.error("公钥加载失败. filePath:{}, {}", filePath, e.getMessage());
        }
    }

    private String readFile(String fileName) {
        try {
            File f = new File(fileName);
            FileInputStream in = new FileInputStream(f);
            int len = (int) f.length();

            byte[] data = new byte[len];
            int read = 0;
            while (read < len) {
                read += in.read(data, read, len - read);
            }
            in.close();
            return new String(data);
        } catch (IOException e) {
        }
        return null;
    }
}
